package com.bocomm.tracevisualization.utils;

import lombok.extern.slf4j.Slf4j;

import java.security.SecureRandom;
import java.util.Random;

/**
 * 雪花算法生成器
 */
@Slf4j
public class SnowFlakeGenerator {
    //机器id
    private long workerId;
    //占用5个bit位
    private long workerIdBit = 10L;
    /**
     * @doc 最大的workerId = 11111 = 31 一共32个
     */
    private long maxWorkerId = ~(-1 << workerIdBit);

    //自增序列的最大长度 12位
    private long sequenceBits = 12L;

    //递增开始的序列
    private long sequence = 0L;

    private static final Random RANDOM = new Random();

    public SnowFlakeGenerator(long roomId, long workerId, long sequence){
        if(workerId>maxWorkerId||workerId<0){
            throw new IllegalArgumentException("work id 错误");
        }

        this.workerId =workerId;
        this.sequence = sequence;

    }
    public SnowFlakeGenerator(long workerId){
        this.workerId = checkWorkerId(workerId);
        log.debug("worker starting. timestamp left shift {}, worker id {}", timestampLeftShift, workerId);
    }
    private long checkWorkerId(long workerId) {
        // sanity check for workerId
        if (workerId > maxWorkerId || workerId < 0) {
            int rand = new SecureRandom().nextInt((int) maxWorkerId + 1);
            log.warn("worker Id can't be greater than {} or less than 0, use a random {}", maxWorkerId, rand);
            return rand;
        }

        return workerId;
    }
    //记录sequence能存储的最大长度
    private long sequenceMask = ~(-1L<<sequenceBits);
    //存储上次生成的id的时间戳
    private long lastTimestamp =-1L;
    // 初始时间值
    private long twepoch =1602841121769L;

    private long workerIdShift = sequenceBits;
    private long timestampLeftShift = sequenceBits + workerIdBit ;

    public synchronized long nextVal(){
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            /*log.error("clock is moving backwards.  Rejecting requests until {}.", lastTimestamp);
            throw new RuntimeException(String.format(
                    "Clock moved backwards.  Refusing to generate id for {} milliseconds", lastTimestamp - timestamp));*/
            try {

                long offset = lastTimestamp - timestamp;
                wait(offset << 1);
                timestamp = timeGen();
                if (timestamp < lastTimestamp) {
                    throw new RuntimeException("时间戳异常");
                }
            } catch (InterruptedException e) {
                log.error("wait interrupted");
                throw new RuntimeException("等待超时");
            }
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                //seq 为0的时候表示是下一毫秒时间开始对seq做随机
                sequence = RANDOM.nextInt(100);
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            //如果是新的ms开始
            sequence = RANDOM.nextInt(100);
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
    }
    protected long timeGen() {
        return System.currentTimeMillis();
    }
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());
    }

}